import type { Dayjs } from "dayjs";
import { useAtomValue } from "jotai";
import { useNavigate, useParams, useSearchParams } from "react-router";
import { useEffect, useState } from "react";
import type { EChartsCoreOption } from "echarts";
import { Button, DatePicker, Radio, Switch, type DatePickerProps } from "antd";
import Chart, { useEchartsInstance } from "@/components/Chart";
import {
  getDeviceChartHistory,
  getDeviceRunningChartRealtime,
} from "@/lib/api/deviceStatus";
import useFetch from "@/lib/hooks/useFetch";
import { useWebSocket } from "@/providers/WebsocketProvider";
import { tokenAtom } from "@/store/user";
import dayjs from "dayjs";

type RealtimeData = [string, string, ...number[]];

export default function Charts() {
  const [mode, setMode] = useState<"realtime" | "history">(() => {
    const condition = sessionStorage.getItem("history-chart-view-condition");
    if (condition) {
      return "history";
    }
    return "realtime";
  });
  const { websocket } = useWebSocket();
  const params = useParams<{ id: string }>();
  const token = useAtomValue(tokenAtom);
  const [isMonitor, setIsMonitor] = useState(false);
  const [chartData, setChartData] = useState<EChartsCoreOption | null>(null);
  const [historyRange, setHistoryRange] = useState<[Dayjs, Dayjs] | null>(null);
  const [historyAutoRange, setHistoryAutoRange] = useState<
    "last30" | "last60" | "last120" | null
  >("last30");
  const [interval, setInterval] = useState(5);
  const echarts = useEchartsInstance();
  const navigate = useNavigate();

  const deviceId = params.id!;

  function handleNavigateToHistory() {
    const range = getDateRange(historyRange || historyAutoRange);
    navigate({
      pathname: "/setting/history",
      search: `?deviceId=${deviceId}&date=${range.map((item) => item.format("YYYY-MM-DD HH:mm:ss"))}`,
    });
  }

  const { refetch: loadChartRealtime, cancel: cancelLoadChartRealtime } =
    useFetch({
      fetchFn: (
        params: Parameters<typeof getDeviceRunningChartRealtime>[0],
        signal: AbortSignal,
      ) =>
        getDeviceRunningChartRealtime(params, token, signal).then((res) => {
          if (res.code !== 200) {
            throw res;
          }
          return res.data;
        }),
      onSuccess(data) {
        echarts.current!.clear();
        setChartData(normalizeData(data));
        websocket.emit("BEGIN_CHARTS", { deviceId });
      },
    });

  const { refetch: loadChartHistory, cancel: cancelLoadChartHistory } =
    useFetch({
      fetchFn: (
        data: Parameters<typeof getDeviceChartHistory>[0],
        signal: AbortSignal,
      ) =>
        getDeviceChartHistory(data, token, signal).then((res) => {
          if (res.code !== 200) {
            throw res;
          }
          return res.data;
        }),
      onSuccess(data) {
        echarts.current!.clear();
        setChartData({
          ...normalizeData(data),
          // 区域选择
          brush: {
            toolbox: ["lineX", "clear"], // 定义工具箱按钮
            xAxisIndex: 0, // 绑定到第一个 x 轴
            throttleType: "debounce",
            throttleDelay: 300,
          },
        });
      },
    });

  useEffect(() => {
    if (mode === "realtime") {
      function onData(data: RealtimeData) {
        setChartData((p) => appendRealtimeData(p, data));
      }
      function onConnected() {
        loadChartRealtime({
          deviceId: parseInt(deviceId),
          isMonitor: isMonitor ? "1" : "0",
        });
      }
      websocket.on("CHARTS_ON_TIME", onData);
      websocket.on("connected", onConnected);
      return () => {
        cancelLoadChartRealtime();
        websocket.emit("END_CHARTS");
        websocket.off("CHARTS_ON_TIME", onData);
        websocket.off("connected", onConnected);
      };
    } else {
      const condition = JSON.parse(
        sessionStorage.getItem("history-chart-view-condition") || "null",
      ) as { date: string[] } | null;
      let _range = historyRange;
      if (condition) {
        _range = [dayjs(condition.date[0]), dayjs(condition.date[1])];
        setHistoryAutoRange(null);
        setHistoryRange(_range);
        // todo: 兼容 StrictMode
        sessionStorage.removeItem("history-chart-view-condition");
        return;
      }
      const range = getDateRange(_range || historyAutoRange);
      loadChartHistory({
        deviceId: parseInt(deviceId),
        interval,
        startTime: range[0].format("YYYY-MM-DD HH:mm:ss"),
        endTime: range[1].format("YYYY-MM-DD HH:mm:ss"),
        isMonitor: isMonitor ? "1" : "0",
      });

      const instance = echarts.current!;
      // 绘制自定义图形显示摘要信息
      function handleDrawSelectionSummary(params: any) {
        const coordRange = params.batch[0].areas[0]?.coordRange;
        const options = instance.getOption() as any;
        const graphicId = "summary";
        if (!coordRange) {
          if (
            (options.graphic?.[0]?.elements as any[])?.some(
              (item) => item.id === graphicId,
            )
          ) {
            // ? 直接删除组无效
            instance.setOption({
              graphic: options.graphic[0].elements
                .map((item: any) => ({
                  type: item.type,
                  id: item.id,
                  parentId: item.parentId,
                }))
                .filter((item: any) => item.parentId === graphicId)
                .map((item: any) => ({
                  type: item.type,
                  id: item.id,
                  $action: "remove",
                })),
            });
          }
          return;
        }

        const duration =
          (new Date(options.xAxis[0].data[coordRange[1]]).getTime() -
            new Date(options.xAxis[0].data[coordRange[0]]).getTime()) /
          60000;
        const data = (options.series as any[]).map((item) => ({
          color: item.lineStyle.color,
          text: `${item.name}: ${((item.data[coordRange[1]] - item.data[coordRange[0]]) / duration).toFixed(1)}/M`,
        }));
        instance.setOption({
          graphic: [
            {
              id: graphicId,
              type: "group",
              left: "10%",
              top: "5%",
              children: [
                {
                  type: "rect",
                  z: 100,
                  left: "0",
                  top: "0",
                  shape: {
                    width: 140,
                    height: data.length * 22,
                  },
                  style: {
                    fill: "rgba(0,0,0,0.8)",
                    stroke: "#555",
                    lineWidth: 1,
                    shadowBlur: 8,
                    shadowOffsetX: 3,
                    shadowOffsetY: 3,
                    shadowColor: "rgba(0,0,0,0.2)",
                  },
                },
                ...data.map((item, index) => ({
                  type: "text",
                  z: 100,
                  left: 10,
                  top: index * 20 + 10,
                  style: {
                    fill: item.color,
                    width: 220,
                    overflow: "break",
                    text: item.text,
                    font: "14px Microsoft YaHei",
                  },
                })),
              ],
            },
          ],
        });
      }
      instance.on("brushSelected", handleDrawSelectionSummary);
      return () => {
        cancelLoadChartHistory();
        instance.off("brushSelected", handleDrawSelectionSummary);
      };
    }
  }, [
    deviceId,
    interval,
    websocket,
    mode,
    isMonitor,
    loadChartRealtime,
    cancelLoadChartRealtime,
    historyRange,
    historyAutoRange,
    loadChartHistory,
    cancelLoadChartHistory,
    echarts,
  ]);

  return (
    <div className="flex h-full flex-col px-8 pb-14 pt-4">
      <div className="mb-2 flex items-center">
        <Radio.Group
          buttonStyle="solid"
          defaultValue={mode}
          onChange={(e) => setMode(e.target.value)}
        >
          <Radio.Button value="realtime">实时</Radio.Button>
          <Radio.Button value="history">历史</Radio.Button>
        </Radio.Group>
        <Switch
          className="ml-2"
          checkedChildren="测量点"
          unCheckedChildren="测量点"
          checked={isMonitor}
          onChange={setIsMonitor}
        />
        {mode === "history" && (
          <div className="mx-auto flex items-center gap-x-5 text-sm">
            <Radio.Group
              value={historyAutoRange}
              onChange={(e) => {
                setHistoryRange(null);
                setHistoryAutoRange(e.target.value);
              }}
              buttonStyle="solid"
            >
              <Radio.Button value="last30">30分钟</Radio.Button>
              <Radio.Button value="last60">1小时</Radio.Button>
              <Radio.Button value="last120">2小时</Radio.Button>
            </Radio.Group>
            <DatePicker.RangePicker
              value={historyRange}
              onChange={(v) => {
                setHistoryAutoRange(null);
                setHistoryRange(
                  v && v[0] && v[1] ? (v as [Dayjs, Dayjs]) : null,
                );
              }}
              disabledDate={disableDate(10)}
              showTime
            />
            <div>
              <span>时间间隔：</span>
              <Radio.Group
                value={interval}
                onChange={(e) => setInterval(e.target.value)}
                buttonStyle="solid"
              >
                <Radio.Button value={5}>5</Radio.Button>
                <Radio.Button value={15}>15</Radio.Button>
                <Radio.Button value={30}>30</Radio.Button>
              </Radio.Group>
            </div>
            <Button onClick={handleNavigateToHistory}>在历史页面查看</Button>
          </div>
        )}
      </div>
      <Chart
        className="min-h-0 flex-auto"
        options={chartData}
        echarts={echarts}
      />
    </div>
  );
}

function normalizeData(data: EChartsCoreOption) {
  const externalXId = 1;
  return {
    ...data,
    xAxis: (data.xAxis as any[]).map((item) =>
      item.id === externalXId
        ? {
            ...item,
            axisLabel: {
              ...item.axisLabel,
              interval(index: number, value: string) {
                return value !== "null" && !!value;
              },
            },
            axisTick: {
              ...item.axisTick,
              interval(index: number, value: string) {
                return value !== "null" && !!value;
              },
            },
            splitLine: {
              ...item.splitLine,
              interval(index: number, value: string) {
                return value !== "null" && !!value;
              },
            },
          }
        : item,
    ),
  };
}

function appendRealtimeData(options: any, data: RealtimeData) {
  const [label1, label2, ...value] = data;
  const label = [label1, label2];

  return {
    ...options,
    xAxis: (options.xAxis as any[]).map((item, index) => ({
      ...item,
      data: item.data.concat(label[index]),
    })),
    series: options.series.map((item: any, index: number) => ({
      ...item,
      data: item.data.concat(value[index]),
    })),
  };
}

const getYearMonth = (date: Dayjs) => date.year() * 12 + date.month();
function disableDate(len: number): DatePickerProps["disabledDate"] {
  return function (current, { from, type }) {
    if (from) {
      const minDate = from.add(-len, "days");
      const maxDate = from.add(len, "days");

      switch (type) {
        case "year":
          return (
            current.year() < minDate.year() || current.year() > maxDate.year()
          );

        case "month":
          return (
            getYearMonth(current) < getYearMonth(minDate) ||
            getYearMonth(current) > getYearMonth(maxDate)
          );

        default:
          return Math.abs(current.diff(from, "days")) >= len;
      }
    }

    return false;
  };
}

function getDateRange(
  range: [Dayjs, Dayjs] | "last30" | "last60" | "last120" | null,
) {
  let res: [Dayjs, Dayjs];
  if (Array.isArray(range)) {
    res = range;
  } else {
    const now = dayjs(new Date());
    if (range === "last30") {
      res = [now.add(-30, "minutes"), now];
    } else if (range === "last60") {
      res = [now.add(-1, "hour"), now];
    } else {
      res = [now.add(-2, "hours"), now];
    }
  }

  return res;
}
